/////////////////////////////////////////////////////////////////////////////
// Purpose:     Timers management
// Author:      Jesus Gonzalez
// Modified by:
// Copyright:   (c) 2003-2013 Jesus Gonzalez <jgonzalez@gdr-sistemas.com>
// License:     BSD License
/////////////////////////////////////////////////////////////////////////////

#ifndef _SMARTLIB_TIMER_H_
#define _SMARTLIB_TIMER_H_

/**
 * \file
 * Timers management.
 */

#include <SmartLib/ExtendedTypes.h>
#include <SmartLib/SmartPtr.h>

class Timer;
class TimerPrvSys;

///@defgroup timers Timers
///@{

/**
 * The ITimerHandler abstract class defines the interface for timer handlers.
 *
 * To implement a timer handler, define your own derived class implementing the
 * @ref OnTimer() method.
 *
 * @see Timer. TimerEx
 */
class ITimerHandler
{
public:
    virtual ~ITimerHandler() {};
    /**
     * Callback method that is called when the timer to which this handler is associated is triggered.
     *
     * @param[in] timer Timer that was triggered.
     */
    virtual void OnTimer(Timer& timer) = 0;
};

/**
 * The TimerHandler abstract class is the base class for timer handler implementations.
 *
 * Use this base class when you want the timer to which the handler will be associated to
 * dynamically manage deallocation.
 *
 * To implement a dynamically managed timer handler, define your own derived class implementing
 * the @ref OnTimer() method.
 */
class TimerHandler : public SmartObject, public ITimerHandler
{
public:
    DECLARE_SMARTPTR(TimerHandler);
};

/**
 * The Timer class can be used to trigger events at regular intervals.
 */
class Timer : public SmartObject
{
public:
    DECLARE_SMARTPTR_NEWCTOR(Timer);

    /**
     * Default constructor.
     */
    Timer();

    /**
     * Destructor.
     */
    virtual ~Timer();

    /**
     * Starts the timer with the given period in milliseconds.
     *
     * Note: Timers that do not have assigned a handler can only be started in 'one shot' mode.
     *
     * @param[in] period_ms Period in milliseconds.
     * @param[in] one_shot If \p true, the timer will be triggered only once,
     *            otherwise it will be triggered periodically.
     */
    void Start(uint period_ms, bool one_shot);

    /**
     * Stops the timer.
     */
    void Stop();

    /**
     * Checks if the timer period has expired.
     *
     * This method is intended to be used with 'one shot' timers without assigned handler.
     *
     * @return \p True if the timer has expired, \p false otherwise.
     */
    bool isExpired() const;

    /**
     * Sets the handler for this timer.
     *
     * If the \a invokeThread parameter is false, the timer handler will interrupt the
     * process' main thread to be executed. Otherwise, the timer handler will be
     * executed in its own thread.
     *
     * The optional \a timerId parameter can be used to differentiate
     * timers when the same handler is used for different timers.
     *
     * @remark
     * Handlers set using this method will be dynamically managed, and thus can be deleted
     * when the timer is deleted (if necessary).
     *
     * @param[in] handler Handler for timer events.
     * @param[in] invokeThread If \p true, the handler will be invoked in a new thread, otherwise
     *                          it will be invoked as an interruption of the main thread.
     * @param[in] timerId User-assigned identifier.
     */
    void setHandler(const TimerHandler::Ptr &handler, bool invokeThread = false, int timerId = 0);

    /**
     * Sets the handler for this timer.
     *
     * If the \a invokeThread parameter is \p false, the timer handler will interrupt the
     * process' main thread to be executed. Otherwise, the timer handler will be
     * executed in its own thread.
     *
     * The optional \a timerId parameter can be used to differentiate
     * timers when the same handler is used for different timers.
     *
     * @remark
     * Handlers set using this method won't be dynamically managed.
     *
     * @param[in] handler Handler for timer events.
     * @param[in] invokeThread If \p true, the handler will be invoked in a new thread, otherwise
     *                          it will be invoked as an interruption of the main thread.
     * @param[in] timerId User-assigned identifier.
     */
    void setHandler(ITimerHandler* handler, bool invokeThread = false, int timerId = 0);

    /**
     * @cond PRIVATE
     * \b NOTE: Internal use only
     */
    static void _SignalHandler(Timer* timer);
    /// @endcond

    /**
     * Gets the user-assigned identifier.
     *
     * @return User-assigned identifier.
     */
    int getTimerId() const
    {
        return m_timerId;
    }

    /**
     * Suspends the execution of the current thread until the given time has elapsed.
     *
     * @param[in] time_ms Suspension time in milliseconds.
     */
    static void Pause(uint time_ms);

private:
    TimerPrvSys* m_prvsys;
    ITimerHandler* m_handler;
    TimerHandler::Ptr m_handlerPtr;
    int m_timerId;
    bool m_invokeThread;

    void Init();
};

/**
 * The TimerEx abstract class is the base class for implementations of timers that handle
 * its own trigger events.
 *
 * To used this class, define your own derived class implementing the @ref OnTimer() method,
 * which will be called each time that the timer is triggered.
 *
 * @see Timer
 */
class TimerEx : public Timer, public ITimerHandler
{
public:
    DECLARE_SMARTPTR(TimerEx);

protected:
    /**
     * Default constructor.
     *
     * If the \a invokeThread parameter is \p false, the timer handler will interrupt the
     * process' main thread to be executed. Otherwise, the timer handler will be
     * executed in its own thread.
     *
     * @param[in] invokeThread If \p true, the handler will be invoked in a new thread, otherwise
     *                          it will be invoked as an interruption of the main thread.
     */
    TimerEx(bool invokeThread = false);
};

///@}

#endif /* _SMARTLIB_TIMER_H_ */
